package com.soyea.service;

import com.google.common.collect.Lists;
import com.soyea.beans.CacheKeyConstants;
import com.soyea.common.RequestHolder;
import com.soyea.dao.SysAclMapper;
import com.soyea.dao.SysRoleAclMapper;
import com.soyea.dao.SysRoleUserMapper;
import com.soyea.dao.SysUserMapper;
import com.soyea.model.SysAcl;
import com.soyea.model.SysUser;
import com.soyea.util.JsonMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.codehaus.jackson.type.TypeReference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.Serializable;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 用来获取各种权限列表
 */
@Service
@Transactional
@Slf4j
public class SysCoreService {

	@Resource
	private SysAclMapper sysAclMapper;
	@Resource
	private SysRoleUserMapper sysRoleUserMapper;
	@Resource
	private SysRoleAclMapper sysRoleAclMapper;
//    @Resource
//    private SysCacheService sysCacheService;

	@Autowired
	private SysUserMapper sysUserMapper;

	/**
	 * 获取当前用户的权限列表
	 *
	 * @return
	 */
	public List<SysAcl> getCurrentUserAclList() {
		int userId = RequestHolder.getCurrentUser().getId();
		return getUserAclList(userId);
	}

	/**
	 * 获取指定角色下的所有权限列表
	 *
	 * @param roleId
	 * @return
	 */
	public List<SysAcl> getRoleAclList(int roleId) {
		//通过角色id列表查询出所有权限id列表
		List<Integer> aclIdList = sysRoleAclMapper.getAclIdListByRoleIdList(Lists.<Integer>newArrayList(roleId));
		if (CollectionUtils.isEmpty(aclIdList)) {
			return Lists.newArrayList();
		}
		//根据权限id列表查询出相应的权限列表
		return sysAclMapper.getByIdList(aclIdList);
	}

	/**
	 * 获取当前用户的权限列表
	 *
	 * 思路:
	 * 如果是超级管理员,则返回所有权限
	 * 否则先根据userId,查询出roleIdList
	 * select roleId from role_user where userId=#{userId}
	 * 再根据roleIdList查询出所有的权限Id列表
	 * select aclId from role_acl where roleId in (roleIdList)
	 * 最后根据权限Id列表查询出所有权限
	 * select * from acl where aclId in #{aclIdList}
	 *
	 * @param userId
	 * @return
	 */
	public List<SysAcl> getUserAclList(int userId) {
		if (isSuperAdmin()) {
			//如果是超级管理员,则返回所有权限
			return sysAclMapper.getAll();
		}

		/*
		1.通过userId->roleIdList  user_role表
		2.通过roleIdList->aclIdList role_acl表
		3.通过aclIdList -> aclList acl表
		 */
		//查询出指定用户的角色列表
		List<Integer> userRoleIdList = sysRoleUserMapper.getRoleIdListByUserId(userId);
		if (CollectionUtils.isEmpty(userRoleIdList)) {
			return Lists.newArrayList();
		}
		//通过角色id列表查询出所有权限id列表
		List<Integer> userAclIdList = sysRoleAclMapper.getAclIdListByRoleIdList(userRoleIdList);
		if (CollectionUtils.isEmpty(userAclIdList)) {
			return Lists.newArrayList();
		}
		//根据权限id列表查询出相应的权限列表
		return sysAclMapper.getByIdList(userAclIdList);
	}

	/**
	 * 判断是否是超级管理员
	 *
	 * @return
	 */
	public boolean isSuperAdmin() {
		// 这里是我自己定义了一个假的超级管理员规则，实际中要根据项目进行修改
		// 可以是配置文件获取，可以指定某个用户，也可以指定某个角色
		SysUser sysUser = RequestHolder.getCurrentUser();
		if (sysUser.getMail().contains("admin")) {
			return true;
		}
		return false;
	}

	/**
	 * 判断该URL是否有访问权限.
	 * 规则：只要有一个权限点有权限，那么我们就认为有访问权限
	 *
	 * 思路
	 * 如果是超级管理员,则直接通过,返回true
	 * 根据url返回权限列表,如果权限列表为空,说明不需要拦截该url,返回true
	 * 获取当前用户的权限id列表
	 * 遍历权限列表,寻找有效的权限.若找不到有效的权限,说明不需要拦截,直接返回true
	 * 若找到有效的权限,判断当前用户的权限id列表中是否含有该权限,有则返回true
	 * 其余情况一律返回false
	 *
	 * @param url
	 * @return
	 */
	public boolean hasUrlAcl(String url) {

		//如果是超级管理员,则直接通过
		if (isSuperAdmin()) {
			log.info("该用户是超级管理员,拥有所有权限");
			return true;
		}

		//获取指定url的权限列表 ,需要判断是否为Null 和 权限是否有效
		List<SysAcl> aclList = sysAclMapper.getByUrl(url);
		if (CollectionUtils.isEmpty(aclList)) {
//	          为空说明找不到url,说明该URL不需要拦截.
			log.info("找不到URL={}的权限,不需要拦截",url);
			return true;
		}

		//获取当前用户的权限列表
		//List<SysAcl> userAclList = getCurrentUserAclListFromCache(); //从缓存中获取
		List<SysAcl> userAclList = getUserAclList(RequestHolder.getCurrentUser().getId());

		//获取当前用户的权限Id列表
		Set<Integer> userAclIdSet = userAclList.stream().map(acl -> acl.getId()).collect(Collectors.toSet());

		boolean hasValidAcl = false;
		// 规则：只要有一个权限点有权限，那么我们就认为有访问权限
		for (SysAcl acl : aclList) {
			// 判断一个用户是否具有某个权限点的访问权限
			if (acl == null || acl.getStatus() != 1) { // 权限点无效
				continue;
			}
			//找到指定的url的权限,然后判断用户是否有该权限
			hasValidAcl = true;
			if (userAclIdSet.contains(acl.getId())) {
				log.info("用户包含该权限,放行");
				return true;
			}
		}

//	      没有有效的权限,说明不需要拦截.
		if (!hasValidAcl) {
			log.info(" 没有有效的权限,不需要拦截.");
			return true;
		}
		return false;
	}

	/**
	 * 从缓存中获取当前用户的权限列表
	 *
	 * 思路
	 * 获取当前用户id
	 * 从缓存中获取当前用户的权限列表
	 * 若权限列表为空,查询数据库,获取当前用户的权限列表,若它不为空,则缓存到redis,并返回
	 *
	 * @return
	 */
//    public List<SysAcl> getCurrentUserAclListFromCache() {
//        int userId = RequestHolder.getCurrentUser().getId();
//        String cacheValue = sysCacheService.getFromCache(CacheKeyConstants.USER_ACLS, String.valueOf(userId));
//        if (StringUtils.isBlank(cacheValue)) {
//            List<SysAcl> aclList = getCurrentUserAclList();
//            if (CollectionUtils.isNotEmpty(aclList)) {
//                sysCacheService.saveCache(JsonMapper.obj2String(aclList), 600, CacheKeyConstants.USER_ACLS, String.valueOf(userId));
//            }
//            return aclList;
//        }
//        return JsonMapper.string2Obj(cacheValue, new TypeReference<List<SysAcl>>() {
//        });
//    }
}
